﻿// Copyright (c) Yoganand R. All rights reserved.
// For continued development and updates, visit http://yogsbreath.net
using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Web.UI;
using System.Text;
using System.Text.RegularExpressions;

namespace MVCnExt.Web.UI.Ext
{
    public class ExtComboBox : Field 
    {
        public override List<ScriptReference> GetScriptReferences()
        {
            return base.GetScriptReferences();
        }

        public override IList<ScriptDescriptor> GetScriptDescriptors()
        {
            IList<ScriptDescriptor> list = base.GetScriptDescriptors();

            ScriptControlDescriptorBase desc = (ScriptControlDescriptorBase)list[0];

            desc.Type = "Ext.form.ComboBox";

            String[] properties = new String[]{
                "allQuery", "displayField", "editable", "forceSelection", "handleHeight", "hiddenName", "lazyRender", "listAlign",
                "listClass", "listWidth", "loadingText", "maxHeight", "minHeight", "minChars", "minListWidth", "minListWidth",
                "pageSize", "queryDelay", "queryParam", "resizable", "selectedClass", "enableShadow", "selectOnFocus", 
                "transform", "title", "typeAhead", "typeAheadDelay", "valueField", "valueNotFoundText", "triggerAction", "mode" 
            };

            Type component = typeof(ExtComboBox);

            foreach (String property in properties)
            {
                PropertyInfo i = component.GetProperty(property);

                if (i == null)
                    throw new InvalidOperationException(String.Format("Property not found, 'ExtComboBox.{0}'", property));

                Object value = i.GetValue(this, null);
                
                //get the default value attribute
                if (value == null)
                {
                    // Gets the attributes for the property.
                    System.ComponentModel.AttributeCollection attributes =
                       TypeDescriptor.GetProperties(this)[property].Attributes;

                    /* get the default value by retrieving the DefaultValueAttribute 
                       from the AttributeCollection. */
                    DefaultValueAttribute defaultAttribute =
                       (DefaultValueAttribute)attributes[typeof(DefaultValueAttribute)];

                    if (defaultAttribute != null)
                        value = defaultAttribute.Value;
                }                

                if (value == null)
                {
                    continue;
                }             

                if (!(value is String) && !(value is TriggerAction) && !(value is DataLoadMode))
                    desc.AddPropertyBase(property, value);
                else if ((value is TriggerAction) || (value is DataLoadMode))
                    desc.AddPropertyBase(property, String.Format("'{0}'", value.ToExtString()));
                else
                    desc.AddPropertyBase(property, String.Format("'{0}'", value));
            }

            //add selection model
            if (store != null)
            {
                // build items list
                StringBuilder tabList = new StringBuilder();

                IList<ScriptDescriptor> descriptors = store.GetScriptDescriptors();

                // write the item's config to the list
                tabList.Append(((ExtScriptControlDescriptor)descriptors[0]).WriteScript(ScriptType.NewOnly));

                desc.AddPropertyBase("store", tabList);
            }

            //add selection model
            if (simpleStore != null)
            {
                // build items list
                StringBuilder tabList = new StringBuilder();

                IList<ScriptDescriptor> descriptors = simpleStore.GetScriptDescriptors();

                // write the item's config to the list
                tabList.Append(((ExtScriptControlDescriptor)descriptors[0]).WriteScript(ScriptType.NewOnly));

                desc.AddPropertyBase("store", tabList);
            }

            return list;
        }

        /// <summary>
        /// The text query to send to the server to return all records for the list with no filtering (defaults to '').
        /// </summary>
        [Category("Config Options")]
        [Description("The text query to send to the server to return all records for the list with no filtering (defaults to '').")]
        public String allQuery { get; set; }

        /// <summary>
        /// Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server).
        /// </summary>
        [DefaultValue(DataLoadMode.Remote)]
        [Description("Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server).")]
        public DataLoadMode mode { get; set; }

        /// <summary>
        /// The underlying data field name to bind to this ComboBox (defaults to undefined if mode = 'remote' or 'text' if transforming a select).
        /// </summary>        
        [Category("Config Options")]
        [Description("The underlying data field name to bind to this ComboBox (defaults to undefined if mode = 'remote' or 'text' if transforming a select).")]
        public String displayField { get; set; }

        /// <summary>
        /// The action to execute when the trigger field is activated. Use 'All' to run the query specified by the allQuery config option (defaults to 'Query').
        /// </summary>
        [DefaultValue(TriggerAction.Query)]
        [Description("The action to execute when the trigger field is activated. Use 'All' to run the query specified by the allQuery config option (defaults to 'Query').")]
        public TriggerAction triggerAction { get; set; }

        /// <summary>
        /// False to prevent the user from typing text directly into the field, just like a traditional select (defaults to true).
        /// </summary>
        [Category("Behavior")]
        [DefaultValue(true)]
        [Description("False to prevent the user from typing text directly into the field, just like a traditional select (defaults to true).")]
        public Boolean? editable { get; set; }

        /// <summary>
        /// True to restrict the selected value to one of the values in the list, false to allow the user to set arbitrary text into the field (defaults to false).
        /// </summary>
        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("True to restrict the selected value to one of the values in the list, false to allow the user to set arbitrary text into the field (defaults to false).")]
        public Boolean? forceSelection { get; set; }

        /// <summary>
        /// The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8).
        /// </summary>
        [Category("Config Options")]
        [DefaultValue("8")]
        [Description("The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8).")]
        public Unit? handleHeight { get; set; }

        /// <summary>
        /// If specified, a hidden form field with this name is dynamically generated to store the field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically post during a form submission.
        /// </summary>
        [Description("If specified, a hidden form field with this name is dynamically generated to store the field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically post during a form submission.")]
        public String hiddenName { get; set; }

        /// <summary>
        /// True to prevent the ComboBox from rendering until requested (should always be used when rendering into an Ext.Editor, defaults to false).
        /// </summary>
        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("True to prevent the ComboBox from rendering until requested (should always be used when rendering into an Ext.Editor, defaults to false).")]
        public Boolean? lazyRender { get; set; }

        /// <summary>
        /// A valid anchor position value. See Ext.Element.alignTo for details on supported anchor positions (defaults to 'tl-bl').
        /// </summary>
        [Category("Config Options")]
        [Description("A valid anchor position value. See Ext.Element.alignTo for details on supported anchor positions (defaults to 'tl-bl').")]
        public String listAlign { get; set; }

        /// <summary>
        /// CSS class to apply to the dropdown list element (defaults to '').
        /// </summary>
        [Category("Config Options")]
        [Description("CSS class to apply to the dropdown list element (defaults to '').")]
        public String listClass { get; set; }

        /// <summary>
        /// The width in pixels of the dropdown list (defaults to the width of the ComboBox field).
        /// </summary>
        [Category("Config Options")]
        [Description("The width in pixels of the dropdown list (defaults to the width of the ComboBox field).")]
        public Unit? listWidth { get; set; }

        /// <summary>
        /// The text to display in the dropdown list while data is loading. Only applies when mode = 'remote' (defaults to 'Loading...').
        /// </summary>
        [Category("Config Options")]
        [DefaultValue("Loading...")]
        [Localizable(true)]
        [Description("The text to display in the dropdown list while data is loading. Only applies when mode = 'remote' (defaults to 'Loading...').")]
        public String loadingText { get; set; }

        /// <summary>
        /// The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300).
        /// </summary>        
        [Category("Config Options")]
        [DefaultValue("300")]
        [Description("The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300).")]
        public Unit? maxHeight { get; set; }

        /// <summary>
        ///  The minimum height in pixels of the dropdown list when the list is constrained by its distance to the viewport edges (defaults to 90).
        /// </summary>  
        [Category("Config Options")]
        [DefaultValue("90")]
        [Description("The minimum height in pixels of the dropdown list when the list is constrained by its distance to the viewport edges (defaults to 90).")]
        public Unit? minHeight { get; set; }

        /// <summary>
        /// The minimum number of characters the user must type before autocomplete and typeahead activate (defaults to 4 if remote or 0 if local, does not apply if editable = false).
        /// </summary>
        [DefaultValue(4)]
        [Description("The minimum number of characters the user must type before autocomplete and typeahead activate (defaults to 4 if remote or 0 if local, does not apply if editable = false).")]
        public String minChars { get; set; }

        /// <summary>
        /// The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if listWidth has a higher value).
        /// </summary>
        [DefaultValue(typeof(Unit), "70")]
        [Description("The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if listWidth has a higher value).")]
        public String minListWidth { get; set; }

        public String boxLabel { get; set; }

        /// <summary>
        /// If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the filter queries will execute with page addToStart and limit parameters. Only applies when mode = 'remote' (defaults to 0).
        /// </summary>
        [DefaultValue(0)]
        [Description("If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the filter queries will execute with page addToStart and limit parameters. Only applies when mode = 'remote' (defaults to 0).")]
        public int? pageSize { get; set; }

        /// <summary>
        /// The length of time in milliseconds to delay between the addToStart of typing and sending the query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local').
        /// </summary>
        [DefaultValue(500)]
        [Description("The length of time in milliseconds to delay between the addToStart of typing and sending the query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local').")]
        public String queryDelay { get; set; }

        /// <summary>
        /// Name of the query as it will be passed on the querystring (defaults to 'query').
        /// </summary>
        [DefaultValue("query")]
        [Description("Name of the query as it will be passed on the querystring (defaults to 'query').")]
        public String queryParam { get; set; }

        [DefaultValue(false)]
        [Description("True to add a resize handle to the bottom of the dropdown list (defaults to false)")]
        public Boolean? resizable { get; set; }

        /// <summary>
        /// CSS class to apply to the selected items in the dropdown list (defaults to 'x-combo-selected').
        /// </summary>
        [Description("CSS class to apply to the selected items in the dropdown list (defaults to 'x-combo-selected').")]
        public String selectedClass { get; set; }

        [DefaultValue(true)]
        public Boolean? enableShadow { get; set; }

        /// <summary>
        /// True to automatically select any existing field text when the field receives input focus (defaults to false).
        /// </summary>
        [Description("True to automatically select any existing field text when the field receives input focus (defaults to false).")]
        public Boolean? selectOnFocus { get; set; }

        /// <summary>
        /// The ID of an existing select to convert to a ComboBox.
        /// </summary>
        [Description("The ID of an existing select to convert to a ComboBox.")]
        public String transform { get; set; }

        [Description("If supplied, a header element is created containing this text and added into the top of the dropdown list.")]
        public String title { get; set; }

        /// <summary>
        /// True to populate and autoselect the remainder of the text being typed after a configurable delay (typeAheadDelay) if it matches a known value (defaults to false).
        /// </summary>
        [Description("True to populate and autoselect the remainder of the text being typed after a configurable delay (typeAheadDelay) if it matches a known value (defaults to false).")]
        public Boolean? typeAhead { get; set; }

        /// <summary>
        /// The length of time in milliseconds to wait until the typeahead text is displayed if TypeAhead = true (defaults to 250).
        /// </summary>
        [DefaultValue(250)]
        [Description("The length of time in milliseconds to wait until the typeahead text is displayed if TypeAhead = true (defaults to 250).")]
        public String typeAheadDelay { get; set; }

        /// <summary>
        /// The underlying data value name to bind to this ComboBox (defaults to undefined if mode = 'remote' or 'value' if transforming a select) Note: use of a valueField requires the user to make a selection in order for a value to be mapped.
        /// </summary>
        [Description("The underlying data value name to bind to this ComboBox (defaults to undefined if mode = 'remote' or 'value' if transforming a select) Note: use of a valueField requires the user to make a selection in order for a value to be mapped.")]
        public String valueField { get; set; }

        /// <summary>
        /// When using a name/value combo, if the value passed to setValue is not found in the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined).
        /// </summary>
        [Description("When using a name/value combo, if the value passed to setValue is not found in the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined).")]
        public String valueNotFoundText { get; set; }

        /// <summary>
        /// The data store to use.
        /// </summary>
        [Description("The data store to use.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(ExtContainer))]
        [TemplateInstance(TemplateInstance.Single)]
        public ExtStore store { get; set; }

        /// <summary>
        /// The data store to use.
        /// </summary>
        [Description("The data store to use.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(ExtContainer))]
        [TemplateInstance(TemplateInstance.Single)]
        public ExtSimpleStore simpleStore { get; set; }
    }
}
